/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "zlang_in.h"

int Keyword_foreach( struct ZlangRuntime *rt )
{
	struct ZlangTokenDataUnitHeader		*token_info1 = NULL ;
	char					*token1 = NULL ;
	struct ZlangTokenDataUnitHeader		*token_info2 = NULL ;
	char					*token2 = NULL ;
	struct ZlangTokenDataUnitHeader		*token_info3 = NULL ;
	char					*token3 = NULL ;
	struct ZlangTokenDataUnitHeader		*token_info4 = NULL ;
	char					*token4 = NULL ;
	struct ZlangTokenDataUnitHeader		*token_info11 = NULL ;
	char					*token11 = NULL ;
	struct ZlangTokenDataUnitHeader		*token_info21 = NULL ;
	char					*token21 = NULL ;
	struct ZlangInterpretStatementContext	foreach_interpret_statement_ctx ;
	struct ZlangObject			*parent_obj = NULL ;
	struct ZlangObject			*collect_obj = NULL ;
	struct ZlangObject			*iterator_obj = NULL ;
	struct ZlangObject			*iter_obj = NULL ;
	struct ZlangObject			*from_obj = NULL ;
	struct ZlangObject			*to_obj = NULL ;
	struct ZlangObject			*result_obj = NULL ;
	unsigned char				logic_result ;
	struct ZlangObject			*obj = NULL ;
	struct ZlangTokenDataPageHeader		*foreach_body_token_datapage_header = NULL ;
	char					*foreach_body_token_dataunit = NULL ;
	struct ZlangFunction			*bak_in_func = NULL ;
	
	int					nret = 0 ;
	
	TEST_RUNTIME_DEBUG_THEN_PRINT_ENTER_FUNCTION(rt)
	
	TRAVELTOKEN_AND_SAVEINFO( rt , token_info1 , token1 )
	if( token_info1->token_type != TOKEN_TYPE_BEGIN_OF_SUB_EXPRESSION ) /* ( */
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_SYNTAX , "expect '(' but '%s'" , rt->travel_token )
		TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
		return ZLANG_ERROR_SYNTAX;
	}
	
	TRAVELTOKEN_AND_SAVEINFO( rt , token_info2 , token2 )
	if( token_info2->token_type != TOKEN_TYPE_IDENTIFICATION ) /* parent_obj IDENTIFICATION */
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_SYNTAX , "expect IDENTIFICATION but '%s'" , rt->travel_token )
		TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
		return ZLANG_ERROR_SYNTAX;
	}
	
	QueryCharsetAliasAndChangeTokenInfo( rt , token_info2 , & token2 );
	
	parent_obj = QueryObjectByObjectName( rt , token2 ) ;
	if( parent_obj == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_OBJECT_NOT_IMPORTED_OR_DECLARED , "object '%s' not imported or declared" , token2 )
		TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
		return ZLANG_ERROR_OBJECT_NOT_IMPORTED_OR_DECLARED;
	}
	
	TRAVELTOKEN_AND_SAVEINFO( rt , token_info3 , token3 )
	if( token_info3->token_type == TOKEN_TYPE_IDENTIFICATION ) /* obj IDENTIFICATION */
	{
		obj = QueryObjectInStatementSegment( rt , token3 ) ;
		TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "QueryObjectInStatementSegment obj[%s] return " , token3 ); DebugPrintObject( rt , obj ); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
		if( obj )
		{
			SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_OBJECT_HAD_DECLARED , "object '%s' had declared" , token3 )
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return ZLANG_ERROR_OBJECT_HAD_DECLARED;
		}
		
		iter_obj = CloneObjectInLocalStack( rt , token3 , parent_obj ) ;
		if( iter_obj == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return nret;
		}
		else
		{
			TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "CreateObjectInLocalStack ok , " ); DebugPrintObject(rt,iter_obj); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
		}
		
		TRAVELTOKEN_AND_SAVEINFO( rt , token_info4 , token4 )
		if( token_info4->token_type != TOKEN_TYPE_IN && token_info4->token_type != TOKEN_TYPE_FROM ) /* in or from */
		{
			SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_SYNTAX , "expect 'in' but '%s'" , rt->travel_token )
			CleanObject( rt , iter_obj );
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return ZLANG_ERROR_SYNTAX;
		}
	}
	else if( token_info3->token_type == TOKEN_TYPE_IN || token_info3->token_type == TOKEN_TYPE_FROM ) /* in or from */
	{
		token_info4 = token_info3 ;
		token4 = token3 ;
		
		iter_obj = parent_obj ;
	}
	else
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_SYNTAX , "expect IDENTIFICATION or 'in' but '%s'" , rt->travel_token )
		TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
		return ZLANG_ERROR_SYNTAX;
	}
	
	if( token_info4->token_type == TOKEN_TYPE_IN )
	{
		TEST_RUNTIME_DEBUG( rt )
		{
			TOKENTYPE_TO_STRING( rt->travel_token_info->token_type , _zlang_token_type_str )
			PRINT_TABS_AND_FORMAT( rt , "CALL InterpretExpression , last_token[%s][%s]" , _zlang_token_type_str , rt->travel_token )
		}
		memset( & foreach_interpret_statement_ctx , 0x00 , sizeof(struct ZlangInterpretStatementContext) );
		foreach_interpret_statement_ctx.token_of_expression_end1 = TOKEN_TYPE_END_OF_SUB_EXPRESSION ;
		nret = InterpretExpression( rt , & foreach_interpret_statement_ctx , & collect_obj ) ;
		TEST_RUNTIME_DEBUG( rt )
		{
			TOKENTYPE_TO_STRING( rt->travel_token_info->token_type , _zlang_token_type_str )
			TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "InterpretExpression return[%d] last_token[%s][%s] " , nret , _zlang_token_type_str , rt->travel_token ); DebugPrintObject(rt,collect_obj); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
		}
		if( nret != ZLANG_INFO_END_OF_EXPRESSION )
		{
			if( iter_obj != parent_obj )
				CleanObject( rt , iter_obj );
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return nret;
		}
		
		foreach_body_token_datapage_header = rt->travel_token_datapage_header ;
		foreach_body_token_dataunit = rt->travel_token_dataunit ;
		
		iterator_obj = CloneIteratorObject( rt , NULL ) ;
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CloneIteratorObjectInTmpStack ok" )
		
		CallRuntimeFunction_iterator_TravelFirst( rt , iterator_obj , collect_obj );
		for( ; CallRuntimeFunction_iterator_IsTravelOver(rt,iterator_obj) == 0 ; CallRuntimeFunction_iterator_TravelNext(rt,iterator_obj) )
		{
			nret = CallRuntimeFunction_iterator_GetElement( rt , iterator_obj , & obj ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CallRuntimeFunction_iterator_GetElement failed[%d]" , nret )
				DestroyObject( rt , iterator_obj );
				if( iter_obj != parent_obj )
					CleanObject( rt , iter_obj );
				TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
				return nret;
			}
			
			TEST_RUNTIME_DEBUG( rt )
			{
				TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "CallRuntimeFunction_iterator_GetElement ok , " ); DebugPrintObject(rt,obj); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
			}
			
			TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "ReferObjectPropertiesEntity " ); DebugPrintObject(rt,obj); printf( " to " ); DebugPrintObject(rt,iter_obj); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
			nret = ReferObjectPropertiesEntity( rt , iter_obj , obj ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ReferObjectPropertiesEntity failed[%d]" , nret )
				DestroyObject( rt , iterator_obj );
				if( iter_obj != parent_obj )
					CleanObject( rt , iter_obj );
				TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
				return nret;
			}
			
			TEST_RUNTIME_DEBUG( rt )
			{
				PRINT_TABS(rt) printf( "ReferObjectPropertiesEntity ok , iter_obj " ); DebugPrintObject( rt , iter_obj ); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE
			}
			
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "foreach( . ) ..." )
			
			PEEKTOKEN_AND_SAVEINFO( rt , token_info11 , token11 )
			if( token_info11->token_type == TOKEN_TYPE_BEGIN_OF_STATEMENT_SEGMENT )
			{
				NEXTTOKEN( rt )
				
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CALL InterpretStatementSegment" )
				IncreaseStackInfo( rt , NULL );
				bak_in_func = rt->in_func ;
				rt->in_func = NULL ;
				nret = InterpretStatementSegment( rt ) ;
				rt->in_func = bak_in_func ;
				DecreaseStackInfo( rt );
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "InterpretStatementSegment return[%d]" , nret )
				if( nret == ZLANG_INFO_END_OF_STATEMENT_SEGMENT )
				{
					rt->travel_token_datapage_header = foreach_body_token_datapage_header ;
					rt->travel_token_dataunit = foreach_body_token_dataunit ;
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "rego foreach( ... )" )
					continue;
				}
				else if( nret == ZLANG_INFO_CONTINUE )
				{
					rt->travel_token_datapage_header = foreach_body_token_datapage_header ;
					rt->travel_token_dataunit = foreach_body_token_dataunit ;
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "continue foreach( ... )" )
					continue;
				}
				else if( nret == ZLANG_INFO_BREAK )
				{
					NEXTTOKEN( rt )
					rt->travel_token_datapage_header = foreach_body_token_datapage_header ;
					rt->travel_token_dataunit = foreach_body_token_dataunit ;
					break;
				}
				else
				{
					DestroyObject( rt , iterator_obj );
					if( iter_obj != parent_obj )
						CleanObject( rt , iter_obj );
					TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
					return nret;
				}
			}
			else
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CALL InterpretStatement" )
				nret = InterpretStatement( rt ) ;
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "InterpretStatement return[%d]" , nret )
				if( nret == ZLANG_INFO_END_OF_STATEMENT )
				{
					rt->travel_token_datapage_header = foreach_body_token_datapage_header ;
					rt->travel_token_dataunit = foreach_body_token_dataunit ;
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "rego foreach( ... )" )
					continue;
				}
				else if( nret == ZLANG_INFO_CONTINUE )
				{
					rt->travel_token_datapage_header = foreach_body_token_datapage_header ;
					rt->travel_token_dataunit = foreach_body_token_dataunit ;
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "continue foreach( ... )" )
					continue;
				}
				else if( nret == ZLANG_INFO_BREAK )
				{
					rt->travel_token_datapage_header = foreach_body_token_datapage_header ;
					rt->travel_token_dataunit = foreach_body_token_dataunit ;
					nret = SkipStatement( rt ) ;
					if( nret != ZLANG_INFO_END_OF_STATEMENT )
					{
						TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
						return nret;
					}
				}
				else
				{
					DestroyObject( rt , iterator_obj );
					if( iter_obj != parent_obj )
						CleanObject( rt , iter_obj );
					TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
					return nret;
				}
			}
		}
		
		DestroyObject( rt , iterator_obj );
		if( iter_obj != parent_obj )
			CleanObject( rt , iter_obj );
	}
	else if( token_info4->token_type == TOKEN_TYPE_FROM )
	{
		int		step ;
		
		TEST_RUNTIME_DEBUG( rt )
		{
			TOKENTYPE_TO_STRING( rt->travel_token_info->token_type , _zlang_token_type_str )
			PRINT_TABS_AND_FORMAT( rt , "CALL InterpretExpression , last_token[%s][%s]" , _zlang_token_type_str , rt->travel_token )
		}
		memset( & foreach_interpret_statement_ctx , 0x00 , sizeof(struct ZlangInterpretStatementContext) );
		foreach_interpret_statement_ctx.token_of_expression_end1 = TOKEN_TYPE_TO ;
		nret = InterpretExpression( rt , & foreach_interpret_statement_ctx , & from_obj ) ;
		TEST_RUNTIME_DEBUG( rt )
		{
			TOKENTYPE_TO_STRING( rt->travel_token_info->token_type , _zlang_token_type_str )
			TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "InterpretExpression return[%d] last_token[%s][%s] " , nret , _zlang_token_type_str , rt->travel_token ); DebugPrintObject(rt,from_obj); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
		}
		if( nret != ZLANG_INFO_END_OF_EXPRESSION )
		{
			if( iter_obj != parent_obj )
				CleanObject( rt , iter_obj );
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return nret;
		}
		
		TEST_RUNTIME_DEBUG( rt )
		{
			TOKENTYPE_TO_STRING( rt->travel_token_info->token_type , _zlang_token_type_str )
			PRINT_TABS_AND_FORMAT( rt , "CALL InterpretExpression , last_token[%s][%s]" , _zlang_token_type_str , rt->travel_token )
		}
		memset( & foreach_interpret_statement_ctx , 0x00 , sizeof(struct ZlangInterpretStatementContext) );
		foreach_interpret_statement_ctx.token_of_expression_end1 = TOKEN_TYPE_END_OF_SUB_EXPRESSION ;
		nret = InterpretExpression( rt , & foreach_interpret_statement_ctx , & to_obj ) ;
		TEST_RUNTIME_DEBUG( rt )
		{
			TOKENTYPE_TO_STRING( rt->travel_token_info->token_type , _zlang_token_type_str )
			TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "InterpretExpression return[%d] last_token[%s][%s] " , nret , _zlang_token_type_str , rt->travel_token ); DebugPrintObject(rt,to_obj); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
		}
		if( nret != ZLANG_INFO_END_OF_EXPRESSION )
		{
			if( iter_obj != parent_obj )
				CleanObject( rt , iter_obj );
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return nret;
		}
		
		if( ! IsTypeOf( rt , from_obj , iter_obj ) )
		{
			if( IsObjectPropertiesEntityNull( from_obj ) )
			{
				SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_FOREACH_FROM_IS_NULL , "foreach from is null" )
				return ZLANG_ERROR_FOREACH_FROM_IS_NULL;
			}
			
			obj = from_obj ;
			
			nret = TryAscensionType( rt , iter_obj , & obj ) ;
			if( nret == 0 )
			{
				SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_OPERATOR_TYPE_NOT_MATCHED , "from_obj[%s]->ancestor_name[%s] type not matched with iter obj[%s]->ancestor_name[%s] type" , GetObjectName(obj) , GetCloneObjectName(obj) , GetObjectName(iter_obj) , GetCloneObjectName(iter_obj) )
				return ZLANG_ERROR_OPERATOR_TYPE_NOT_MATCHED;
			}
			else if( nret == 2 )
			{
				TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "TryAscensionType " ); DebugPrintObject(rt,obj); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
			}
			else
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "TryAscensionType obj[%s] and obj[%s] failed" , GetObjectName(iter_obj) , GetObjectName(obj) )
				return GET_RUNTIME_ERROR_NO(rt);
			}
			CleanObject( rt , from_obj );
			
			from_obj = obj ;
		}
		
		if( ! IsTypeOf( rt , to_obj , iter_obj ) )
		{
			if( IsObjectPropertiesEntityNull( to_obj ) )
			{
				SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_FOREACH_TO_IS_NULL , "foreach to is null" )
				return ZLANG_ERROR_FOREACH_TO_IS_NULL;
			}
			
			obj = to_obj ;
			
			nret = TryAscensionType( rt , iter_obj , & obj ) ;
			if( nret == 0 )
			{
				SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_OPERATOR_TYPE_NOT_MATCHED , "to_obj[%s]->ancestor_name[%s] type not matched with iter obj[%s]->ancestor_name[%s] type" , GetObjectName(obj) , GetCloneObjectName(obj) , GetObjectName(iter_obj) , GetCloneObjectName(iter_obj) )
				return ZLANG_ERROR_OPERATOR_TYPE_NOT_MATCHED;
			}
			else if( nret == 2 )
			{
				TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "TryAscensionType " ); DebugPrintObject(rt,obj); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
			}
			else
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "TryAscensionType obj[%s] and obj[%s] failed" , GetObjectName(iter_obj) , GetObjectName(obj) )
				return GET_RUNTIME_ERROR_NO(rt);
			}
			CleanObject( rt , to_obj );
			
			to_obj = obj ;
		}
		
		foreach_body_token_datapage_header = rt->travel_token_datapage_header ;
		foreach_body_token_dataunit = rt->travel_token_dataunit ;
		
		nret = EvalObject( rt , iter_obj , from_obj ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "EvalObject iter_obj from from_obj failed[%d]" , nret )
			if( iter_obj != parent_obj )
				CleanObject( rt , iter_obj );
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return nret;
		}
		else
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "EvalObject iter_obj from from_obj ok" )
		}
		
		result_obj = CloneBoolObject( rt , NULL ) ;
		if( result_obj == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CloneBoolObject result_obj failed" )
			if( iter_obj != parent_obj )
				CleanObject( rt , iter_obj );
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return nret;
		}
		else
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CloneBoolObject result_obj ok" )
		}
		
		nret = Compare_LT( rt , to_obj , from_obj , & result_obj ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "Compare_LT failed[%d]" , nret )
			if( iter_obj != parent_obj )
				CleanObject( rt , iter_obj );
			DestroyObject( rt , result_obj );
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return nret;
		}
		else
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "Compare_LT to_obj from_obj ok" )
			
			CallRuntimeFunction_bool_GetBoolValue( rt , result_obj , & logic_result );
			if( logic_result == TRUE )
			{
				step = -1 ;
			}
			else
			{
				nret = Compare_GT( rt , to_obj , from_obj , & result_obj ) ;
				if( nret )
				{
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "Compare_GT failed[%d]" , nret )
					if( iter_obj != parent_obj )
						CleanObject( rt , iter_obj );
					DestroyObject( rt , result_obj );
					TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
					return nret;
				}
				else
				{
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "Compare_GT to_obj from_obj ok" )
					
					CallRuntimeFunction_bool_GetBoolValue( rt , result_obj , & logic_result );
					if( logic_result == TRUE )
					{
						step = 1 ;
					}
					else
					{
						step = 0 ;
					}
				}
			}
		}
		
		for( ; ; )
		{
			TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "iter_obj " ); DebugPrintObject(rt,iter_obj); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
			
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "foreach( . ) ..." )
			
			PEEKTOKEN_AND_SAVEINFO( rt , token_info11 , token11 )
			if( token_info11->token_type == TOKEN_TYPE_BEGIN_OF_STATEMENT_SEGMENT )
			{
				NEXTTOKEN( rt )
				
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CALL InterpretStatementSegment" )
				IncreaseStackInfo( rt , NULL );
				bak_in_func = rt->in_func ;
				rt->in_func = NULL ;
				nret = InterpretStatementSegment( rt ) ;
				rt->in_func = bak_in_func ;
				DecreaseStackInfo( rt );
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "InterpretStatementSegment return[%d]" , nret )
				if( nret == ZLANG_INFO_END_OF_STATEMENT_SEGMENT )
				{
					rt->travel_token_datapage_header = foreach_body_token_datapage_header ;
					rt->travel_token_dataunit = foreach_body_token_dataunit ;
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "rego foreach( ... )" )
				}
				else if( nret == ZLANG_INFO_CONTINUE )
				{
					rt->travel_token_datapage_header = foreach_body_token_datapage_header ;
					rt->travel_token_dataunit = foreach_body_token_dataunit ;
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "continue foreach( ... )" )
				}
				else if( nret == ZLANG_INFO_BREAK )
				{
					NEXTTOKEN( rt )
					rt->travel_token_datapage_header = foreach_body_token_datapage_header ;
					rt->travel_token_dataunit = foreach_body_token_dataunit ;
					break;
				}
				else
				{
					if( iter_obj != parent_obj )
						CleanObject( rt , iter_obj );
					DestroyObject( rt , result_obj );
					TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
					return nret;
				}
			}
			else
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CALL InterpretStatement" )
				nret = InterpretStatement( rt ) ;
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "InterpretStatement return[%d]" , nret )
				if( nret == ZLANG_INFO_END_OF_STATEMENT )
				{
					rt->travel_token_datapage_header = foreach_body_token_datapage_header ;
					rt->travel_token_dataunit = foreach_body_token_dataunit ;
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "rego foreach( ... )" )
				}
				else if( nret == ZLANG_INFO_CONTINUE )
				{
					rt->travel_token_datapage_header = foreach_body_token_datapage_header ;
					rt->travel_token_dataunit = foreach_body_token_dataunit ;
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "continue foreach( ... )" )
				}
				else if( nret == ZLANG_INFO_BREAK )
				{
					rt->travel_token_datapage_header = foreach_body_token_datapage_header ;
					rt->travel_token_dataunit = foreach_body_token_dataunit ;
					nret = SkipStatement( rt ) ;
					if( nret != ZLANG_INFO_END_OF_STATEMENT )
					{
						TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
						return nret;
					}
					break;
				}
				else
				{
					if( iter_obj != parent_obj )
						CleanObject( rt , iter_obj );
					DestroyObject( rt , result_obj );
					TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
					return nret;
				}
			}
			
			if( step == 0 )
			{
				rt->travel_token_datapage_header = foreach_body_token_datapage_header ;
				rt->travel_token_dataunit = foreach_body_token_dataunit ;
				break;
			}
			else if( step == 1 )
			{
				nret = Compare_GE( rt , iter_obj , to_obj , & result_obj ) ;
				if( nret )
				{
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "Compare_GT failed[%d]" , nret )
					if( iter_obj != parent_obj )
						CleanObject( rt , iter_obj );
					DestroyObject( rt , result_obj );
					TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
					return nret;
				}
				else
				{
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "Compare_GT ok" )
				}
				
				CallRuntimeFunction_bool_GetBoolValue( rt , result_obj , & logic_result );
				if( logic_result == TRUE )
				{
					rt->travel_token_datapage_header = foreach_body_token_datapage_header ;
					rt->travel_token_dataunit = foreach_body_token_dataunit ;
					break;
				}
				
				nret = UnaryOperator_PLUS_PLUS( rt , iter_obj ) ;
				if( nret )
				{
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "UnaryOperator_PLUS_PLUS failed[%d]" , nret )
					if( iter_obj != parent_obj )
						CleanObject( rt , iter_obj );
					DestroyObject( rt , result_obj );
					TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
					return nret;
				}
				else
				{
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "UnaryOperator_PLUS_PLUS ok" )
				}
			}
			else if( step == -1 )
			{
				nret = Compare_LE( rt , iter_obj , to_obj , & result_obj ) ;
				if( nret )
				{
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "Compare_LT failed[%d]" , nret )
					if( iter_obj != parent_obj )
						CleanObject( rt , iter_obj );
					DestroyObject( rt , result_obj );
					TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
					return nret;
				}
				else
				{
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "Compare_LT ok" )
				}
				
				CallRuntimeFunction_bool_GetBoolValue( rt , result_obj , & logic_result );
				if( logic_result == TRUE )
				{
					rt->travel_token_datapage_header = foreach_body_token_datapage_header ;
					rt->travel_token_dataunit = foreach_body_token_dataunit ;
					break;
				}
				
				nret = UnaryOperator_MINUS_MINUS( rt , iter_obj ) ;
				if( nret )
				{
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "UnaryOperator_MINUS_MINUS failed[%d]" , nret )
					if( iter_obj != parent_obj )
						CleanObject( rt , iter_obj );
					DestroyObject( rt , result_obj );
					TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
					return nret;
				}
				else
				{
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "UnaryOperator_MINUS_MINUS ok" )
				}
			}
			
		}
		
		if( iter_obj != parent_obj )
			CleanObject( rt , iter_obj );
		DestroyObject( rt , result_obj );
	}
	else
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_INTERNAL , "internal error" )
		TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
		return ZLANG_ERROR_INTERNAL;
	}
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "foreach( ) ." )
	
	PEEKTOKEN_AND_SAVEINFO( rt , token_info21 , token21 )
	if( token_info21->token_type == TOKEN_TYPE_BEGIN_OF_STATEMENT_SEGMENT )
	{
		NEXTTOKEN( rt )
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "foreach( ) { statement_segment. }" )
		
		nret = SkipStatementSegment( rt ) ;
		if( nret != ZLANG_INFO_END_OF_STATEMENT_SEGMENT )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return nret;
		}
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "foreach( ) statement." )
		
		nret = SkipStatement( rt ) ;
		if( nret != ZLANG_INFO_END_OF_STATEMENT )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return nret;
		}
	}
	
	TEST_RUNTIME_DEBUG_THEN_PRINT_LEAVE_FUNCTION(rt)
	return ZLANG_INFO_END_OF_STATEMENT;
}

int SkipStatement_foreach( struct ZlangRuntime *rt )
{
	struct ZlangTokenDataUnitHeader	*token_info1 = NULL ;
	char				*token1 = NULL ;
	struct ZlangTokenDataUnitHeader	*token_info2 = NULL ;
	char				*token2 = NULL ;
	struct ZlangTokenDataUnitHeader	*token_info3 = NULL ;
	char				*token3 = NULL ;
	struct ZlangTokenDataUnitHeader	*token_info4 = NULL ;
	char				*token4 = NULL ;
	struct ZlangTokenDataUnitHeader	*token_info5 = NULL ;
	char				*token5 = NULL ;
	
	int				nret = 0 ;
	
	TEST_RUNTIME_DEBUG_THEN_PRINT_ENTER_FUNCTION(rt)
	
	TRAVELTOKEN_AND_SAVEINFO( rt , token_info1 , token1 )
	if( token_info1->token_type != TOKEN_TYPE_BEGIN_OF_SUB_EXPRESSION ) /* ( */
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_SYNTAX , "expect '(' but '%s'" , rt->travel_token )
		TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
		return ZLANG_ERROR_SYNTAX;
	}
	
	TRAVELTOKEN_AND_SAVEINFO( rt , token_info2 , token2 )
	if( token_info2->token_type != TOKEN_TYPE_IDENTIFICATION ) /* parent_obj IDENTIFICATION */
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_SYNTAX , "expect IDENTIFICATION but '%s'" , rt->travel_token )
		TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
		return ZLANG_ERROR_SYNTAX;
	}
	
	TRAVELTOKEN_AND_SAVEINFO( rt , token_info3 , token3 )
	if( token_info3->token_type == TOKEN_TYPE_IDENTIFICATION ) /* obj IDENTIFICATION */
	{
		TRAVELTOKEN_AND_SAVEINFO( rt , token_info4 , token4 )
		if( token_info4->token_type != TOKEN_TYPE_IN && token_info4->token_type != TOKEN_TYPE_FROM ) /* 'in' or 'from' */
		{
			SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_SYNTAX , "expect 'in' or 'from' but '%s'" , rt->travel_token )
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return ZLANG_ERROR_SYNTAX;
		}
	}
	else if( token_info3->token_type == TOKEN_TYPE_IN || token_info3->token_type == TOKEN_TYPE_FROM ) /* 'in' or 'from' */
	{
		token_info4 = token_info3 ;
		token4 = token3 ;
	}
	else
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_SYNTAX , "expect IDENTIFICATION or 'in' but '%s'" , rt->travel_token )
		TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
		return ZLANG_ERROR_SYNTAX;
	}
	
	if( token_info4->token_type == TOKEN_TYPE_IN )
	{
		TEST_RUNTIME_DEBUG( rt )
		{
			TOKENTYPE_TO_STRING( rt->travel_token_info->token_type , _zlang_token_type_str )
			PRINT_TABS_AND_FORMAT( rt , "CALL SkipExpression , last_token[%s][%s]" , _zlang_token_type_str , rt->travel_token )
		}
		nret = SkipExpression( rt , TOKEN_TYPE_END_OF_SUB_EXPRESSION , 0 , 0 ) ;
		TEST_RUNTIME_DEBUG( rt )
		{
			TOKENTYPE_TO_STRING( rt->travel_token_info->token_type , _zlang_token_type_str )
			TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "SkipExpression return[%d] last_token[%s][%s] " , nret , _zlang_token_type_str , rt->travel_token ); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
		}
		if( nret != ZLANG_INFO_END_OF_EXPRESSION )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return nret;
		}
	}
	else if( token_info4->token_type == TOKEN_TYPE_FROM )
	{
		TEST_RUNTIME_DEBUG( rt )
		{
			TOKENTYPE_TO_STRING( rt->travel_token_info->token_type , _zlang_token_type_str )
			PRINT_TABS_AND_FORMAT( rt , "CALL SkipExpression , last_token[%s][%s]" , _zlang_token_type_str , rt->travel_token )
		}
		nret = SkipExpression( rt , TOKEN_TYPE_TO , 0 , 0 ) ;
		TEST_RUNTIME_DEBUG( rt )
		{
			TOKENTYPE_TO_STRING( rt->travel_token_info->token_type , _zlang_token_type_str )
			TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "SkipExpression return[%d] last_token[%s][%s] " , nret , _zlang_token_type_str , rt->travel_token ); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
		}
		if( nret != ZLANG_INFO_END_OF_EXPRESSION )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return nret;
		}
		
		TEST_RUNTIME_DEBUG( rt )
		{
			TOKENTYPE_TO_STRING( rt->travel_token_info->token_type , _zlang_token_type_str )
			PRINT_TABS_AND_FORMAT( rt , "CALL SkipExpression , last_token[%s][%s]" , _zlang_token_type_str , rt->travel_token )
		}
		nret = SkipExpression( rt , TOKEN_TYPE_END_OF_SUB_EXPRESSION , 0 , 0 ) ;
		TEST_RUNTIME_DEBUG( rt )
		{
			TOKENTYPE_TO_STRING( rt->travel_token_info->token_type , _zlang_token_type_str )
			TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "SkipExpression return[%d] last_token[%s][%s] " , nret , _zlang_token_type_str , rt->travel_token ); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
		}
		if( nret != ZLANG_INFO_END_OF_EXPRESSION )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return nret;
		}
	}
	else
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_SYNTAX , "expect IDENTIFICATION or 'in' but '%s'" , rt->travel_token )
		TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
		return ZLANG_ERROR_SYNTAX;
	}
	
	PEEKTOKEN_AND_SAVEINFO( rt , token_info5 , token5 )
	if( token_info5->token_type == TOKEN_TYPE_BEGIN_OF_STATEMENT_SEGMENT )
	{
		NEXTTOKEN( rt )
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "foreach( ... ) { statement_segment. }" )
		
		nret = SkipStatementSegment( rt ) ;
		if( nret != ZLANG_INFO_END_OF_STATEMENT_SEGMENT )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return nret;
		}
	}
	else
	{
		NEXTTOKEN( rt )
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "foreach( ... ) statement." )
		
		nret = SkipStatement( rt ) ;
		if( nret != ZLANG_INFO_END_OF_STATEMENT )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return nret;
		}
	}

	TEST_RUNTIME_DEBUG_THEN_PRINT_LEAVE_FUNCTION(rt)
	return ZLANG_INFO_END_OF_STATEMENT;
}

